package org.ring.rpc;

import io.netty.buffer.ByteBuf;

import org.ring.common.transport.ByteBufInputStream;
import org.ring.common.transport.ByteBufOutputStream;
import org.ring.common.transport.Message;
import org.ring.common.transport.MessageCodec;
import org.ring.common.utils.reflect.MethodDescriptor;
import org.ring.common.utils.reflect.ServiceDescriptor;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

/**
 * @since 2024-11-01 12:19
 */
public class RpcMessageCodec extends MessageCodec {

    @Override
    protected int encodeData(Message message, ByteBuf buf) throws IOException {
        if (!message.isRequest()) {
            return super.encodeData(message, buf);
        }
        Invocation invocation = (Invocation) message.getData();
        ByteBufOutputStream output = new ByteBufOutputStream(buf);
        DataOutputStream dataOutputStream = new DataOutputStream(output);
        dataOutputStream.writeUTF(invocation.getInterfaceName());
        dataOutputStream.writeUTF(invocation.getMethodName());
        dataOutputStream.writeUTF(invocation.getParamDescriptor());
        Object[] args = invocation.getArgs();
        if (args != null) {
            for (Object argObj : args) {
                serialization.serialize(argObj, output);
            }
        }
        return output.writtenBytes();
    }

    @Override
    protected Object decodeRequestData(ByteBuf buf, int payloadLen) throws IOException {
        Invocation invocation = new Invocation();
        DataInputStream dataInputStream = new DataInputStream(new ByteBufInputStream(buf));
        String serviceName = dataInputStream.readUTF();
        String methodName = dataInputStream.readUTF();
        String paramDescriptor = dataInputStream.readUTF();
        invocation.setInterfaceName(serviceName);
        invocation.setMethodName(methodName);
        invocation.setParamDescriptor(paramDescriptor);

        ServiceDescriptor serviceDescriptor = ServiceRepository.getInstance().lookup(serviceName);
        MethodDescriptor methodDescriptor = serviceDescriptor.getMethod(methodName, paramDescriptor);
        Class[] parameterTypes = methodDescriptor.getParameterTypes();

        if (parameterTypes.length > 0) {
            Object[] args = new Object[parameterTypes.length];
            for (int i = 0; i < parameterTypes.length; i++) {
                args[i] = serialization.deserialize(dataInputStream);
            }
            invocation.setArgs(args);
        }

        return invocation;
    }
}
